﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Indigo=System.IdentityModel.Tokens;

namespace Microsoft.Cryptography.WCF
{
    /// <summary>
    /// Represents an custom assymetric key for X.509 certificates. This custom X509AsymmetricSecurityKey is required to support 
    /// RSA-Signatures with SHA-2 encryption in XML-Signatures
    /// </summary>
    public class X509AsymmetricSecurityKey : Indigo.X509AsymmetricSecurityKey
    {
        X509Certificate2 _cert = null;

        /// <summary>
        /// Represents an custom assymetric key for X.509 certificates. This custom X509AsymmetricSecurityKey is required to support 
        /// RSA-Signatures with SHA-2 encryption in XML-Signatures
        /// </summary>
        /// <param name="cert"><see cref="System.Security.Cryptography.X509Certificates.X509Certificate2"/>, which should be used as security token. </param>
        public X509AsymmetricSecurityKey(X509Certificate2 cert): base(cert)
        {
            _cert = cert;   
        }

        /// <summary>
        /// Returns the de-formatter algorithm for the digital signature, creating an instance of the de-formatter algorithm by 
        /// using CryptoConfig.CreateFromName(algorithm). This approach allows the usage of any de-formatter algorithm, which is part 
        /// of the out-of-the-box .Net Framework or is configured in the machine.config in the '&lt;cryptographySettings&gt;'-section.
        /// </summary>
        /// <param name="algorithm">The identifier/name of the de-formatter algorithm for the digital signature to get an instance of</param>
        /// <returns> Returns the de-formatter algorithm for the digital signature, creating an instance of the de-formatter algorithm by 
        /// using CryptoConfig.CreateFromName(algorithm). This approach allows the usage of any de-formatter algorithm, which is part 
        /// of the out-of-the-box .Net Framework or is configured in the machine.config in the '&lt;cryptographySettings&gt;'-section.</returns>
        public override AsymmetricSignatureDeformatter GetSignatureDeformatter(string algorithm)
        {
            SignatureDescription description = (SignatureDescription)CryptoConfig.CreateFromName(algorithm);

            if (description == null)
            {
                throw new Exception(string.Format("No signature description found for algorithm '{0}'",
                    algorithm));
            }

            AsymmetricSignatureDeformatter deformatter = description.CreateDeformatter(_cert.PublicKey.Key);

            if (deformatter == null)
            {
                throw new Exception(string.Format("Could not create signature deformatter for algorithm '{0}'",
                    algorithm));
            }

            return deformatter;
        }

        /// <summary>
        /// Returns the formatter algorithm for the digital signature, creating the an instance of the formatter algorithm by 
        /// using CryptoConfig.CreateFromName(algorithm). This approach allows the usage of any formatter algorithm, which is part 
        /// of the out-of-the-box .Net Framework or is configured in the machine.config in the '&lt;cryptographySettings&gt;'-section.
        /// </summary>
        /// <param name="algorithm">The identifier/name of the de-formatter algorithm for the digital signature to get an instance of</param>
        /// <returns>Returns the formatter algorithm for the digital signature, creating the an instance of the formatter algorithm by 
        /// using CryptoConfig.CreateFromName(algorithm). This approach allows the usage of any formatter algorithm, which is part 
        /// of the out-of-the-box .Net Framework or is configured in the machine.config in the '&lt;cryptographySettings&gt;'-section.</returns>
        public override AsymmetricSignatureFormatter GetSignatureFormatter(string algorithm)
        {
            
            if (!_cert.HasPrivateKey)
            {
                throw new Exception(string.Format("Certificate 'Subject: {0}, Issuer; {1}, Serial-Number: {2}, Thumbprint: {3}' has no private key. You can't create a signature formatter without private key.",
                    _cert.Subject,
                    _cert.Issuer,
                    _cert.SerialNumber,
                    _cert.Thumbprint));
            }

            SignatureDescription description = (SignatureDescription)CryptoConfig.CreateFromName(algorithm);

            if (description == null)
            {
                throw new Exception(string.Format("No signature description found for algorithm '{0}'",
                    algorithm));
            }
         
            AsymmetricSignatureFormatter formatter = description.CreateFormatter(_cert.PrivateKey);

            if (formatter == null)
            {
                throw new Exception(string.Format("Could not create signature formatter for algorithm '{0}'",
                    algorithm));
            }

            return formatter;
        }
    } 
}
